﻿//==============================================================================
// File: PropertyScope.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Diagnostics;
using WpfRcp.PropertyModel.Core;
using Res = WpfRcp.PropertyModel.Properties.Resources;

namespace WpfRcp.PropertyModel.Scope
{
    /// <summary>
    /// Defines a property scope per instace of the scope.
    /// </summary>
    [Serializable]
    public class PropertyScope : PropertyScopeBase, IDynamicPropertyRegistry
    {
        private readonly MetaPropertyCollection m_MetaProperties;

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyScope"/> class.
        /// </summary>
        public PropertyScope()
        {
            m_MetaProperties = new MetaPropertyCollection();
        }

        #region Cleanup Implementation

        protected override void Dispose(bool disposing)
        {
            try
            {
                m_MetaProperties.Clear();
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        #endregion

        #region IDynamicPropertyRegistry Implementation

        /// <summary>
        /// Registers new dynamic property. Dynamic property is usually added per object instance.
        /// </summary>
        /// <param name="name">Property name. Must be unique in current context.</param>
        /// <param name="propertyType">CLR property type.</param>
        /// <returns>Newly created <see cref="MetaProperty"/>.</returns>
        public MetaProperty RegisterDynamic(string name, Type propertyType)
        {
            if (Disposed)
                throw new ObjectDisposedException(Res.ExceptionObjectAlreadyDisposed);

            return RegisterDynamic(name, propertyType, new PropertyMetadata());
        }

        /// <summary>
        /// Registers new dynamic property. Dynamic property is usually added per object instance.
        /// </summary>
        /// <param name="name">Property name. Must be unique in current context.</param>
        /// <param name="propertyType">CLR property type.</param>
        /// <param name="metadata">Property metadata.</param>
        /// <returns>Newly created <see cref="MetaProperty"/>.</returns>
        public MetaProperty RegisterDynamic(string name, Type propertyType, PropertyMetadata metadata)
        {
            if (Disposed)
                throw new ObjectDisposedException(Res.ExceptionObjectAlreadyDisposed);

            if (IsSealed)
                throw new PropertyScopeException(Res.ExceptionPropertyScopeSealed);

            if (string.IsNullOrEmpty(name))
                throw new MetaPropertyException(Res.ExceptionPropertyNameNull);

            if (m_MetaProperties.Contains(name))
                throw new PropertyScopeException(string.Format(Res.ExceptionNameAlreadyExists, name));

            var metaProperty = PropertyManager.RegisterDynamicScoped(name, propertyType, GetType(), metadata);
            m_MetaProperties.Add(metaProperty);

            OnScopeChanged(metaProperty, null, GetType());

            return metaProperty;
        }

        /// <summary>
        /// Unregisters dynamic property. 
        /// </summary>
        /// <param name="metaProperty">Property to be removed. If property hasn't been previously registered, exception is thrown.</param>
        public void UnregisterDynamic(MetaProperty metaProperty)
        {
            if (Disposed)
                throw new ObjectDisposedException(Res.ExceptionObjectAlreadyDisposed);

            if (IsSealed)
                throw new PropertyScopeException(Res.ExceptionPropertyScopeSealed);

            if (!m_MetaProperties.Contains(metaProperty))
                throw new MetaPropertyException(Res.ExceptionPropertyNotFound);

            bool removed = m_MetaProperties.Remove(metaProperty);
            Debug.Assert(removed);

            OnScopeChanged(null, metaProperty, GetType());
        }

        /// <summary>
        /// Unregisters dynamic property.
        /// </summary>
        /// <param name="name">Name of property to be removed. If property hasn't been previously registered, exception is thrown.</param>
        public void UnregisterDynamic(string name)
        {
            MetaProperty metaProperty = m_MetaProperties.Find(mt => mt.Name == name);
            UnregisterDynamic(metaProperty);
        }

        /// <summary>
        /// Method that returns all dynamic properties registered in the registry.
        /// </summary>
        /// <returns>Returns all dynamic properties registered in the registry.</returns>
        public virtual MetaPropertyCollection GetDynamicProperties()
        {
            return GetDynamicProperties(null);
        }

        #endregion

        #region Public Interface

        /// <summary>
        /// Method that returns all dynamic properties registered in the registry.
        /// </summary>
        /// <param name="container">The container for which <see cref="MetaPropertyCollection"/> is being returned.</param>
        /// <returns>Returns all dynamic properties registered in the registry for given <see cref="IPropertyContainer"/> container.</returns>
        /// <remarks>The <paramref name="container"/> parameter can be null.</remarks>
        public override MetaPropertyCollection GetDynamicProperties(IPropertyContainer container)
        {
            return new MetaPropertyCollection(m_MetaProperties);
        }

        #endregion
    }
}